﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Xml.Serialization;
using System.ComponentModel;
using WcfClientGenerator.Model.Common;

namespace WcfClientGenerator.Model.Config
{
    /// <summary>
    /// The configuration data needed for client proxies generation
    /// </summary>
    [XmlRoot("clientConfiguration")]
    public class WcfClientConfiguration : INotifyPropertyChanged
    {
        #region Events (Public)

        /// <summary>
        /// INotifyPropertyChanged implementation
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Instance Fields (Private)

        /// <summary>
        /// The configuration name
        /// </summary>
        private string name = null;
        /// <summary>
        /// The name of the project containing the service contracts
        /// </summary>
        private string contractsProjectName = null;
        /// <summary>
        /// The names of the projects to receive the generated Asynchronous proxies
        /// </summary>
        private List<string> asyncProxiesDestinationProjects = null;
        /// <summary>
        /// The names of the projects to receive the generated Synchronous proxies
        /// </summary>
        private List<string> syncProxiesDestinationProjects = null;
        /// <summary>
        /// The full name of the class to use as base for the generated proxies
        /// </summary>
        private string clientBaseName = null;
        /// <summary>
        /// The path of the folder where the files will be generated
        /// </summary>
        private string generationFolder = null;
        /// <summary>
        /// The project specific usings section
        /// </summary>
        private string specificUsings = null;
        /// <summary>
        /// The name of the generated file for sync contracts
        /// </summary>
        private string syncContractsFileName = "SyncServiceReferenceContracts.cs";
        /// <summary>
        /// The name of the generated file for sync proxies
        /// </summary>
        private string syncProxiesFileName = "SyncServiceReferenceProxies.cs";
        /// <summary>
        /// The name of the generated file for async contracts
        /// </summary>
        private string asyncContractsFileName = "ServiceReferenceContracts.cs";
        /// <summary>
        /// The name of the generated file for async proxies
        /// </summary>
        private string asyncProxiesFileName = "ServiceReferenceProxies.cs";
        /// <summary>
        /// The name of the generated file for async task proxies
        /// </summary>
        private string asyncTaskProxiesFileName = "ServiceReferenceAsyncTaskProxies.cs";
        /// <summary>
        /// The namespace for the generated service contracts
        /// </summary>
        private string serviceContractsNamespace;
        /// <summary>
        /// The code base of the assembly that contains the template generator
        /// </summary>
        private string templatesAssemblyCodeBase = null;
        /// <summary>
        /// The path of the image associated to the project. NULL to use
        /// a default image
        /// </summary>
        private string tileImage = null;
        /// <summary>
        /// The index of the color to use as the image background
        /// </summary>
        private int tileBackgroundIndex = 0;
        /// <summary>
        /// Indicates whether the generation process is local or remote
        /// </summary>
        private bool useGeneratorService = false;
        /// <summary>
        /// The URL of the generator service
        /// </summary>
        private string generatorServiceUrl = null;

        #endregion

        #region Instance Properties (Public)

        /// <summary>
        /// The configuration name
        /// </summary>
        [XmlElement("name")]
        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                string oldValue = this.name;
                this.name = value;
                if (this.name != oldValue)
                    this.NofifyPropertyChanged("Name");
            }
        }

        /// <summary>
        /// The name of the project containing the service contracts
        /// </summary>
        [XmlElement("contractsProjectName")]
        public string ContractsProjectName
        {
            get
            {
                return this.contractsProjectName;
            }
            set
            {
                string oldValue = this.contractsProjectName;
                this.contractsProjectName = value;
                if (this.contractsProjectName != oldValue)
                    this.NofifyPropertyChanged("ContractsProjectName");
            }
        }

        /// <summary>
        /// The full name of the class to use as base for the generated proxies
        /// </summary>
        [XmlElement("clientBaseName")]
        public string ClientBaseName
        {
            get
            {
                return this.clientBaseName;
            }
            set
            {
                string oldValue = this.clientBaseName;
                this.clientBaseName = value;
                if (this.clientBaseName != oldValue)
                    this.NofifyPropertyChanged("ClientBaseName");
            }
        }

        /// <summary>
        /// The path of the folder where the files will be generated
        /// </summary>
        [XmlElement("generationFolder")]
        public string GenerationFolder
        {
            get
            {
                return this.generationFolder;
            }
            set
            {
                string oldValue = this.generationFolder;
                this.generationFolder = value;
                if (this.generationFolder != oldValue)
                    this.NofifyPropertyChanged("GenerationFolder");
            }
        }

        /// <summary>
        /// The project specific usings section
        /// </summary>
        [XmlElement("specificUsings")]
        public string SpecificUsings
        {
            get
            {
                return this.specificUsings;
            }
            set
            {
                string oldValue = this.specificUsings;
                this.specificUsings = value;
                if (this.specificUsings != oldValue)
                    this.NofifyPropertyChanged("SpecificUsings");
            }
        }

        /// <summary>
        /// The names of the projects to receive the generated Asynchronous proxies in a serializable collection
        /// </summary>
        [XmlArray(ElementName = "asyncDestinationProjects")]
        [XmlArrayItem(ElementName = "destinationProject")]
        public List<string> AsyncDestinationProjects
        {
            get
            {
                return this.asyncProxiesDestinationProjects;
            }
            set
            {
                List<string> oldProjects = this.asyncProxiesDestinationProjects;
                this.asyncProxiesDestinationProjects = value;
                if (WcfClientConfiguration.ListsDiffer(oldProjects, this.asyncProxiesDestinationProjects))
                    this.NofifyPropertyChanged("AsyncDestinationProjects");
            }
        }

        /// <summary>
        /// The names of the projects to receive the generated Synchronous proxies in a serializable collection
        /// </summary>
        [XmlArray(ElementName = "syncDestinationProjects")]
        [XmlArrayItem(ElementName = "destinationProject")]
        public List<string> SyncDestinationProjects
        {
            get
            {
                return this.syncProxiesDestinationProjects;
            }
            set
            {
                List<string> oldProjects = this.syncProxiesDestinationProjects;
                this.syncProxiesDestinationProjects = value;
                if (WcfClientConfiguration.ListsDiffer(oldProjects, this.syncProxiesDestinationProjects))
                    this.NofifyPropertyChanged("SyncDestinationProjects");
            }
        }

        /// <summary>
        /// The name of the generated file for sync contracts
        /// </summary>
        [XmlElement("syncContractsFileName")]
        public string SyncContractsFileName
        {
            get
            {
                return this.syncContractsFileName;
            }
            set
            {
                string oldValue = this.syncContractsFileName;
                this.syncContractsFileName = value;
                if (this.syncContractsFileName != oldValue)
                    this.NofifyPropertyChanged("SyncContractsFileName");
            }
        }

        /// <summary>
        /// The name of the generated file for sync proxies
        /// </summary>
        [XmlElement("syncProxiesFileName")]
        public string SyncProxiesFileName
        {
            get
            {
                return this.syncProxiesFileName;
            }
            set
            {
                string oldValue = this.syncProxiesFileName;
                this.syncProxiesFileName = value;
                if (this.syncProxiesFileName != oldValue)
                    this.NofifyPropertyChanged("SyncProxiesFileName");
            }
        }

        /// <summary>
        /// The name of the generated file for async contracts
        /// </summary>
        [XmlElement("asyncContractsFileName")]
        public string AsyncContractsFileName
        {
            get
            {
                return this.asyncContractsFileName;
            }
            set
            {
                string oldValue = this.asyncContractsFileName;
                this.asyncContractsFileName = value;
                if (this.asyncContractsFileName != oldValue)
                    this.NofifyPropertyChanged("AsyncContractsFileName");
            }
        }

        /// <summary>
        /// The name of the generated file for async proxies
        /// </summary>
        [XmlElement("asyncProxiesFileName")]
        public string AsyncProxiesFileName
        {
            get
            {
                return this.asyncProxiesFileName;
            }
            set
            {
                string oldValue = this.asyncProxiesFileName;
                this.asyncProxiesFileName = value;
                if (this.asyncProxiesFileName != oldValue)
                    this.NofifyPropertyChanged("AsyncProxiesFileName");
            }
        }

        /// <summary>
        /// The name of the generated file for async task proxies
        /// </summary>
        [XmlElement("asyncTaskProxiesFileName")]
        public string AsyncTaskProxiesFileName
        {
            get
            {
                return this.asyncTaskProxiesFileName;
            }
            set
            {
                string oldValue = this.asyncTaskProxiesFileName;
                this.asyncTaskProxiesFileName = value;
                if (this.asyncTaskProxiesFileName != oldValue)
                    this.NofifyPropertyChanged("AsyncTaskProxiesFileName");
            }
        }

        /// <summary>
        /// The namespace for the generated service contracts
        /// </summary>
        [XmlElement("serviceContractsNamespace")]
        public string ServiceContractsNamespace
        {
            get
            {
                return this.serviceContractsNamespace;
            }
            set
            {
                string oldValue = this.serviceContractsNamespace;
                this.serviceContractsNamespace = value;
                if (this.serviceContractsNamespace != oldValue)
                    this.NofifyPropertyChanged("ServiceContractsNamespace");
            }
        }

        /// <summary>
        /// The code base of the assembly that contains the template generator
        /// </summary>
        [XmlElement("templatesAssemblyCodeBase")]
        public string TemplatesAssemblyCodeBase
        {
            get
            {
                return this.templatesAssemblyCodeBase;
            }
            set
            {
                string oldValue = this.templatesAssemblyCodeBase;
                this.templatesAssemblyCodeBase = value;
                if (this.templatesAssemblyCodeBase != oldValue)
                    this.NofifyPropertyChanged("TemplatesAssemblyCodeBase");
            }
        }

        /// <summary>
        /// The path of the image associated to the project. NULL to use
        /// a default image
        /// </summary>
        [XmlElement("tileImage")]
        public string TileImage
        {
            get
            {
                return this.tileImage;
            }
            set
            {
                string oldValue = this.tileImage;
                this.tileImage = value;
                if (this.tileImage != oldValue)
                    this.NofifyPropertyChanged("TileImage");
            }
        }

        /// <summary>
        /// The index of the color to use as the image background
        /// </summary>
        [XmlElement("tileBackgroundIndex")]
        public int TileBackgroundIndex
        {
            get
            {
                return this.tileBackgroundIndex;
            }
            set
            {
                int oldValue = this.tileBackgroundIndex;
                this.tileBackgroundIndex = value;
                if (this.tileBackgroundIndex != oldValue)
                    this.NofifyPropertyChanged("TileBackgroundIndex");
            }
        }

        /// <summary>
        /// Indicates whether the generation process is local or remote
        /// </summary>
        [XmlElement("useGeneratorService")]
        public bool UseGeneratorService
        {
            get
            {
                return this.useGeneratorService;
            }
            set
            {
                bool oldValue = this.useGeneratorService;
                this.useGeneratorService = value;
                if (this.useGeneratorService != oldValue)
                    this.NofifyPropertyChanged("UseGeneratorService");
            }
        }
        /// <summary>
        /// The URL of the generator service
        /// </summary>
        [XmlElement("generatorServiceUrl")]
        public string GeneratorServiceUrl
        {
            get
            {
                return this.generatorServiceUrl;
            }
            set
            {
                string oldValue = this.generatorServiceUrl;
                this.generatorServiceUrl = value;
                if (this.generatorServiceUrl != oldValue)
                    this.NofifyPropertyChanged("GeneratorServiceUrl");
            }
        }
        #endregion

        #region Methods (Public)

        public override string ToString()
        {
            return SerializationManager.XmlSerializeString(this);
        }

        public static WcfClientConfiguration FromString(string xml)
        {
            WcfClientConfiguration configuration = null;
            if (!string.IsNullOrEmpty(xml))
            {
                try
                {
                    configuration = SerializationManager.XmlDeserializeString(xml, typeof(WcfClientConfiguration)) as WcfClientConfiguration;
                }
                catch { }
            }

            return configuration;
        }

        #endregion

        #region Instance Methods (Private)

        private void NofifyPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private static bool ListsDiffer(List<string> list1, List<string> list2)
        {
            bool listsDiffer = false;
            if (list1 == null && list2 != null
                || list1 != null && list2 == null)
            {
                // The new list is null or viceversa
                listsDiffer = true;
            }
            else if (list1 != null && list2 != null)
            {
                // None of the list is null
                if (list1.Count == list2.Count)
                {
                    // Both lists are the same length
                    if (list1.Count > 0 && list2.Count > 0)
                    {
                        // Both lists are non-empty
                        if (list1.Intersect(list2).Count() != list1.Count)
                            // The intersection of the lists is longer or shorter than both lists
                            listsDiffer = true;
                    }
                }
                else
                {
                    // Lists are not the same length
                    listsDiffer = true;
                }
            }

            return listsDiffer;
        }

        #endregion
    }
}
